package com.nakamachizu.controller;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;

import org.mobylet.core.Carrier;
import org.mobylet.core.Mobylet;
import org.mobylet.core.MobyletFactory;
import org.mobylet.core.device.Device;
import org.mobylet.core.device.DeviceDisplay;
import org.mobylet.core.gps.Gps;
import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.controller.validator.Validators;
import org.slim3.datastore.Datastore;
import org.slim3.util.StringUtil;
import org.slim3.util.TimeZoneLocator;

import com.google.appengine.api.datastore.GeoPt;
import com.nakamachizu.collection.ApiKeyMap;
import com.nakamachizu.collection.IconNameMap;
import com.nakamachizu.constant.AppConstant;
import com.nakamachizu.constant.MixiConstant;
import com.nakamachizu.constant.SessionKeyConstant;
import com.nakamachizu.model.LandMarkData;
import com.nakamachizu.model.MapData;
import com.nakamachizu.model.MemberData;
import com.nakamachizu.model.session.MapDataSession;
import com.nakamachizu.service.MapDataService;
import com.nakamachizu.util.ErrorsUtil;
import com.nakamachizu.util.ForwardUtil;
import com.nakamachizu.util.ResourceUtil;

public class MapController extends Controller {

    private static final Logger log = Logger.getLogger(MapController.class.getName());
        
    protected List<String> messages = new ArrayList<String>();
    
    private MapDataService mds;
    private MapDataSession mdses;
    private MapData map;
    private Calendar now;
    private String pass;
    private String keyStr;
    private String zoomLevel;
    private String centerLat;
    private String centerLng;
    private String r;
    private String l;
    private String u;
    private String d;
    
    @Override
    public Navigation run() {
        
        if(!validate()){
            return forward("/error");
        }
        
        this.init();

        if(StringUtil.isEmpty(zoomLevel)){
            zoomLevel = AppConstant.DEFAULT_ZOOM_LEVEL();
        }
        
        if(StringUtil.isEmpty(keyStr)){
            map = mds.getMapDataFromSession(mdses);
        }else{
            map = mds.getMapDataAccessKey(keyStr);
        }

        if(map == null || AppConstant.ACCESSLEVEL_FRIENDS_DENY_FOREIGN().equals(map.getAccessLevel())){
            return forward(ForwardUtil.forwardJsp("nomap.jsp"));
        }
        
        if(!StringUtil.isEmpty(map.getViewPassword())){
            if(StringUtil.isEmpty(pass) && mdses != null){
                pass = mdses.getViewPassword();
            }
            if(!map.getViewPassword().equals(pass)){
                if(!StringUtil.isEmpty(pass)){
                    messages.add("パスワードが違います。");
                }
                requestScope("messages", messages);
                return forward(ForwardUtil.forwardJsp("input_password.jsp"));
            }
        }
        
        Integer w = 600;
        Integer h = 370;
        
        Mobylet mobylet = MobyletFactory.getInstance();
        Carrier carrier = mobylet.getCarrier();
        if(!carrier.equals(Carrier.OTHER)){
            DeviceDisplay display = mobylet.getDisplay();
            Device device = mobylet.getDevice();

            w = display.getWidth();  //ブラウザ幅
            h = display.getHeight(); //ブラウザ高さ
            if(w == null || w.intValue() <= 0){
                w = 100;
            }
            if(h == null || h.intValue() <= 0){
                h = 100;
            }
            h = (int)(h * 0.7);
            
            List<MemberData> memList = map.getMemberDataListRef().getModelList();
            List<LandMarkData> landList = map.getLandMarkDataListRef().getModelList();
            if(StringUtil.isEmpty(centerLat) || StringUtil.isEmpty(centerLng)){
                if(memList != null && memList.size() > 0
                        && !StringUtil.isEmpty(mobylet.getGuid())
                        && mds.getMemberDataUid(memList, mobylet.getGuid()) != null){
                    MemberData m = mds.getMemberDataUid(memList, mobylet.getGuid());
                    centerLat = Float.toString(m.getLatLng().getLatitude());
                    centerLng = Float.toString(m.getLatLng().getLongitude());
                }else if(landList != null && landList.size() > 0){
                    centerLat = Float.toString(landList.get(0).getLatLng().getLatitude());
                    centerLng = Float.toString(landList.get(0).getLatLng().getLongitude());
                }else if(memList != null && memList.size() > 0){
                    centerLat = Float.toString(memList.get(0).getLatLng().getLatitude());
                    centerLng = Float.toString(memList.get(0).getLatLng().getLongitude());
                }else{
                    centerLat = "35.6810976";
                    centerLng = "139.7671169";
                }
            }
            
            
            Gps location = mobylet.getGps();
            
            if(StringUtil.isEmpty(mobylet.getGuid())){
                String guidparam = requestScope("guid");
                if((carrier.equals(Carrier.DOCOMO) && "ON".equals(guidparam))
                     || !carrier.equals(Carrier.DOCOMO)
                   ){
                    messages.add("端末識別情報が取得できませんでした。通知設定を確認してください。");
                }else if(carrier.equals(Carrier.DOCOMO) && StringUtil.isEmpty(guidparam)){
                    return redirect("/map?key=" + keyStr + "&guid=ON");
                }
            }else if(location == null){
                if(memList != null && memList.size() > 0){
                    for(MemberData data : memList){
                        if(mobylet.getGuid().equals(data.getUid())){
                            data.setEditable(true);
                            //data.setKeyStr(KeyFactory.keyToString(data.getKey()));
                        }
                    }
                }
                if(landList != null && landList.size() > 0){
                    for(LandMarkData data : landList){
                        if(mobylet.getGuid().equals(data.getUid())){
                            data.setEditable(true);
                        }
                    }
                }
                //log.info("位置情報が取得できませんでした。");
                //messages.add("位置情報が取得できませんでした。");
            }else{
                DecimalFormat fmt = new DecimalFormat("###.0000000");
                String fmtLat = fmt.format(location.getLat());
                String fmtLng = fmt.format(location.getLon());
                centerLat = fmtLat;
                centerLng = fmtLng;
                GeoPt geo = new GeoPt(Float.parseFloat(fmtLat), Float.parseFloat(fmtLng));
                MemberData mem = mds.getMemberDataUid(memList, mobylet.getGuid());
                if(mem == null){
                    if(memList.size() >= AppConstant.MAX_MEMBER_NUM()){
                        ResourceBundle bundle = ResourceUtil.getResourceBundle(request);
                        ErrorsUtil.putErrors(errors, bundle, "message.error.maxmember", "maxmember");
                    }else{
                        mem = new MemberData();
                        mem.setComment(AppConstant.DEFAULT_COMMENT());
                        String deviceName = null;
                        if(device != null){
                            deviceName = device.getDeviceProfile().get("機種名");
                        }
                        if(StringUtil.isEmpty(deviceName)){
                            deviceName = AppConstant.DEFAULT_NAME();
                        }
                        mem.setName(deviceName);
                        mem.setLatLng(geo);
                        mem.setUid(mobylet.getGuid());
                        mem.setPcIconName(mds.createMemberPcIconName(memList));
                        mem.setMIconName(mds.createMobileMemberIconName(memList));
                        mem.setEntryDate(now.getTime());
                        mem.setModifyDate(now.getTime());
                        mem.getMapDataRef().setModel(map);
                        mem.setEditable(true);
                        String label = IconNameMap.MOBILE_MEMBER_ICON_NAME.get(Integer.toString(memList.size()));
                        messages.add("位置情報を取得しました。あなたの現在のラベルは「" + label.toUpperCase() + "」です。ラベルは参加者の増減で変わります。参加者一覧のあなたのラベルの編集ボタンを押して名前やコメントを変更してください。");
                        memList.add(mem);
                    }
                }else{
                    messages.add("位置情報を更新しました。");
                    mem.setLatLng(geo);
                    mem.setModifyDate(now.getTime());
                    mem.setEditable(true);
                    
                }
                if(mem != null){
                    mem.setKey(Datastore.put(mem));
                    //mem.setKeyStr(KeyFactory.keyToString(mem.getKey()));
                }

            }
            requestScope("landMarkList", landList);
            requestScope("memberList", memList);
        }else{
            requestScope("iconNameMap", IconNameMap.ICON_NAME);
        }
        
        if(!StringUtil.isEmpty(centerLat) && !StringUtil.isEmpty(centerLng)){
            GeoPt center = mds.getCenter(centerLat, centerLng, Float.parseFloat(r), Float.parseFloat(l), Float.parseFloat(u), Float.parseFloat(d));
            requestScope("cLat", center.getLatitude());
            requestScope("cLng", center.getLongitude());
        }
        
        requestScope("now", now.getTime().getTime());
        requestScope("angle", mds.getAngle(zoomLevel, w));
        requestScope("serverName", request.getServerName());
        requestScope("zoom", zoomLevel);
        requestScope("messages", messages);
        requestScope("apiKey", ApiKeyMap.API_KEY_MAP.get(request.getServerName()));
        requestScope("width", w);
        requestScope("height", h);
        requestScope("expire", map.getExpire());
        requestScope("title", map.getTitle());
        if(map.getAdminMailAddress().matches("^[0-9]+" + MixiConstant.MIXI_MAIL_ADDRESS() + "$")){
            requestScope("mixi", true);
            requestScope("accessLevel", map.getAccessLevel());
        }
        mdses = mds.createMapDataSession(map);
        mdses.setViewPassword(pass);
        sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY(), mdses);
        
        return forward(ForwardUtil.forwardJsp("map.jsp"));
    }
    
    protected void init(){
        
        mds = new MapDataService();
        mdses = sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY());
        map = null;
        now = Calendar.getInstance(TimeZoneLocator.get());
        
        pass = requestScope("viewPassword");
        keyStr = requestScope("key");
        zoomLevel = requestScope("zoom");
        centerLat = requestScope("cLat");
        centerLng = requestScope("cLng");
        r = requestScope("r");
        l = requestScope("l");
        u = requestScope("u");
        d = requestScope("d");
        
        if(StringUtil.isEmpty(r)){
            r = "0";
        }
        if(StringUtil.isEmpty(l)){
            l = "0";
        }
        if(StringUtil.isEmpty(u)){
            u = "0";
        }
        if(StringUtil.isEmpty(d)){
            d = "0";
        }
    }
    
    @Override
    protected Navigation handleError(Throwable error) {
        error.printStackTrace();
        log.severe(ErrorsUtil.stackTrace(error));
        return forward("error.jsp");
    }
    
    protected boolean validate() {
        boolean result = false;
        Validators v = new Validators(request);
        String reqKey = requestScope("key");
        if(StringUtil.isEmpty(reqKey)){
            MapDataSession ses = sessionScope(SessionKeyConstant.MAP_DATA_SESSION_KEY());
            if(ses != null){
                requestScope("key", ses.getMapKey());
            }
        }
        v.add("key", v.required());
        v.add("zoom", v.longRange(0, 19));
        v.add("cLat", v.floatType());
        v.add("cLng", v.floatType());
        v.add("cLat", v.doubleRange(-90.0, 90.0));
        v.add("cLng", v.doubleRange(-180.0, 180.0));
        v.add("r", v.floatType());
        v.add("l", v.floatType());
        v.add("u", v.floatType());
        v.add("d", v.floatType());
        v.add("r", v.doubleRange(0.0001, 1.0));
        v.add("l", v.doubleRange(0.0001, 1.0));
        v.add("u", v.doubleRange(0.0001, 1.0));
        v.add("d", v.doubleRange(0.0001, 1.0));
        
        result = v.validate();
        
        return result;
    }
}
